﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Maxcoderz.GameObjects;
using Maxcoderz.LevelEditor;

namespace Maxcoderz
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class MapEditor : Microsoft.Xna.Framework.Game
    {
        private IntPtr _drawMap;
        private EditorForm _form;

        GraphicsDeviceManager _graphics;
        PrimitiveBatch _primitiveBatch;

        // Mouse variables
        Vector2 _mouseLocation;
        Vector2 _previousMouseLocation;
        int _previousMouseScrollValue;
        bool _leftButtonDown;

        // Keyboard variables
        bool _isKeyLock;

        // Map variables
        Map _currentMap = new Map();

        // Selection variables
        Block _selectedBlock;
        
        public MapEditor(EditorForm form)
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            this.IsMouseVisible = true;

            _form = form;
            Mouse.WindowHandle = form.Handle;
            _drawMap = form.GetDrawSurface();
            _graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
            System.Windows.Forms.Control.FromHandle((this.Window.Handle)).VisibleChanged += new EventHandler(MapEditor_VisibleChanged);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            Global.SpriteBatch = new SpriteBatch(GraphicsDevice);

            Global.GlobalTexture = Content.Load<Texture2D>("tilesheet");

            // Add sprites
            // TODO: Load sprite definitions from a file.
            Global.Sprites.Add("base", new Sprite("base", 0, new Rectangle(0, 0, 320, 256)));            

            // Initialize the primitiveBatch
            _primitiveBatch = new PrimitiveBatch(GraphicsDevice);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            HandleMouseInput();

            HandleKeyboardInput();

            base.Update(gameTime);
        }

        private void HandleMouseInput()
        {
            MouseState mouseState = Mouse.GetState();
            _mouseLocation = new Vector2(mouseState.X, mouseState.Y);
            _form.SetMouseLocation(mouseState.X, mouseState.Y);

            bool leftButtonDown = _leftButtonDown;
            _leftButtonDown = (mouseState.LeftButton == ButtonState.Pressed);

            if (!leftButtonDown && _leftButtonDown)
            {
                // User start a new click. Get the top most block of the location of the mouse
                _selectedBlock = _currentMap.GetBlock(_mouseLocation);
            }

            if (leftButtonDown && _leftButtonDown && _selectedBlock != null)
            {
                // User is dragging a block. Update it's location.
                _selectedBlock.Position = new Vector2(
                    _selectedBlock.Position.X - (_previousMouseLocation.X - _mouseLocation.X),
                    _selectedBlock.Position.Y - (_previousMouseLocation.Y - _mouseLocation.Y));
            }

            // update the zoom, if neccesary
            if (_selectedBlock != null && mouseState.ScrollWheelValue != _previousMouseScrollValue)
            {
                _selectedBlock.Zoom += (0.1f * ((mouseState.ScrollWheelValue - _previousMouseScrollValue) / 120));
            }

            _previousMouseLocation = _mouseLocation;
            _previousMouseScrollValue = mouseState.ScrollWheelValue;
        }

        private void HandleKeyboardInput()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            if (!_isKeyLock)
            {
                if (keyboardState.IsKeyDown(Keys.Left) && _selectedBlock != null)
                {
                    // User presses Left, rotate the sprite.
                    _selectedBlock.Rotation -= 0.05f;
                }
                if (keyboardState.IsKeyDown(Keys.Right) && _selectedBlock != null)
                {
                    // User presses Left, rotate the sprite.
                    _selectedBlock.Rotation += 0.05f;
                }
                if (keyboardState.IsKeyDown(Keys.A))
                {
                    _currentMap.Blocks.Add(new Block(Global.Sprites["base"]));
                    _isKeyLock = true;
                }
            }

            if (keyboardState.GetPressedKeys().Length <= 0)
            {
                _isKeyLock = false;
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);                     

            Global.SpriteBatch.Begin(SpriteBlendMode.AlphaBlend);

            _currentMap.Draw();
           
            Global.SpriteBatch.End();

            // Draw a selectionbox around the selected block.
            if (_selectedBlock != null)
            {
                DrawSelectionBox();
            }

            base.Draw(gameTime);
        }

        private void DrawSelectionBox()
        {
            Rectangle _selectRect = _selectedBlock.GetDestinationRectangle();

            _primitiveBatch.Begin(PrimitiveType.LineList);

            _primitiveBatch.AddVertex(new Vector2(_selectRect.X, _selectRect.Y), Color.White);
            _primitiveBatch.AddVertex(new Vector2(_selectRect.X + _selectRect.Width, _selectRect.Y), Color.White);

            _primitiveBatch.AddVertex(new Vector2(_selectRect.X + _selectRect.Width, _selectRect.Y), Color.White);
            _primitiveBatch.AddVertex(new Vector2(_selectRect.X + _selectRect.Width, _selectRect.Y + _selectRect.Height), Color.White);

            _primitiveBatch.AddVertex(new Vector2(_selectRect.X + _selectRect.Width, _selectRect.Y + _selectRect.Height), Color.White);
            _primitiveBatch.AddVertex(new Vector2(_selectRect.X, _selectRect.Y + _selectRect.Height), Color.White);

            _primitiveBatch.AddVertex(new Vector2(_selectRect.X, _selectRect.Y + _selectRect.Height), Color.White);
            _primitiveBatch.AddVertex(new Vector2(_selectRect.X, _selectRect.Y), Color.White);

            _primitiveBatch.End();
        }

        /// <summary>  
        /// Event capturing the construction of a draw surface and makes sure this gets redirected to  
        /// a predesignated drawsurface marked by pointer drawSurface  
        /// </summary>  
        /// <param name="sender"></param>  
        /// <param name="e"></param>  
        void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)  
        {  
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = _drawMap;  
        }  

        /// <summary>  
        /// Occurs when the original gamewindows' visibility changes and makes sure it stays invisible  
        /// </summary>  
        /// <param name="sender"></param>  
        /// <paramm name="e"></param>  
        private void MapEditor_VisibleChanged(object sender, EventArgs e)  
        {  
                if (System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible == true)  
                    System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible = false;  
        }  
    }
}